<template>
  <teleport to="body" :disabled="!appendToBody">
    <transition
      enter-active-class="fade"
      enter-to-class="show"
      leave-from-class="show"
      leave-active-class="fade"
      @before-enter="beforeEnter"
      @after-enter="afterEnter"
      @after-leave="afterLeave"
      :duration="300"
    >
      <div
        class="vb-dialog modal"
        v-show="visible"
        :class="{
          fade: preShowStatic,
          show: preShowStatic,
          'modal-static': showStatic,
        }"
        :style="{
          zIndex: zIndex,
        }"
        @click="onModalClick"
      >
        <div
          class="modal-dialog"
          @transitionend="transitionend"
          :class="[
            scrollable && 'modal-dialog-scrollable',
            centered && 'modal-dialog-centered',
            size && 'modal-' + size,
            fullscreen && 'modal-fullscreen' + (fullscreenSize ? `-${fullscreenSize}-down` : ''),
          ]"
        >
          <div class="modal-content">
            <div class="modal-header" v-if="title">
              <h5 class="modal-title">
                {{ title }}
              </h5>
              <button type="button" class="btn-close" @click="close" v-if="closable"></button>
            </div>
            <div
              class="modal-body d-flex flex-row"
              :class="{
                'vb-dialog-body-i': bodyIcon,
                'align-items-center': bodyIcon,
              }"
            >
              <div v-if="bodyIcon" v-html="bodyIcon" class="vb-dialog-body-ii"></div>
              <div class="flex-fill" v-if="isContentVNode">
                <component :is="content"></component>
              </div>
              <div class="flex-fill" v-html="content" v-else-if="content"></div>
              <div class="flex-fill" v-else>
                <slot></slot>
              </div>
            </div>
            <div class="modal-footer" v-if="innerButtons && innerButtons.length > 0">
              <button
                v-for="(btn, i) in innerButtons"
                :key="i"
                type="button"
                class="btn"
                :class="['btn-' + btn.theme]"
                v-on:click="onBtnClick(btn)"
                :disabled="btn.disabled"
              >
                <i v-if="btn.iconCls" :class="btn.iconCls"></i>
                {{ btn.text }}
              </button>
            </div>
          </div>
        </div>
      </div>
    </transition>
  </teleport>
</template>
<script lang="ts">
import {
  defineComponent,
  ref,
  onBeforeUnmount,
  onBeforeMount,
  watch,
  PropType,
  SetupContext,
  isVNode,
  computed,
  onMounted,
} from "vue";
import Modal from "../modal";
import { DialogButtonProps, DialogOper } from "./dialog.type";
import { UPDATE_MODEL_EVENT } from "../../utils/constants";
import { ButtonTheme } from "../../utils/enums";

const CLOSE_EVENT = "close";
const OPEN_EVENT = "open";
const CLOSED_EVENT = "closed";
const OPENED_EVENT = "opened";
const defaultBtnOptions: DialogButtonProps = {
  close: false,
  theme: ButtonTheme.Secondary,
  handler: function () {},
  text: "",
  iconCls: "",
  disabled: false,
};
function initButtons(buttons: DialogButtonProps[]) {
  const newButtons = [];
  for (let i = 0, len = buttons.length; i < len; i++) {
    const ii = buttons[i];
    const newButton: DialogButtonProps = {};
    for (let j in defaultBtnOptions) {
      if (ii[j] === undefined) {
        newButton[j] = defaultBtnOptions[j];
      } else {
        newButton[j] = ii[j];
      }
    }
    newButtons.push(newButton);
  }
  return newButtons;
}
let DIALOG_ID = 0;
export default defineComponent({
  name: "VbDialog",
  emits: [CLOSE_EVENT, OPEN_EVENT, CLOSED_EVENT, OPENED_EVENT, UPDATE_MODEL_EVENT],
  props: {
    title: {
      type: String,
      default: "",
    },
    content: [String, Object],
    size: {
      default: "",
      validator(v: string) {
        return ["", "sm", "lg", "xl"].indexOf(v) !== -1;
      },
    },
    beforeClose: {
      type: Function as PropType<(...args: any[]) => unknown>,
    },
    buttons: {
      type: Array as PropType<DialogButtonProps[]>,
      default() {
        return [];
      },
    },
    static: {
      type: Boolean,
      default: false,
    },
    scrollable: {
      type: Boolean,
      default: false,
    },
    closable: {
      type: Boolean,
      default: false,
    },
    centered: {
      type: Boolean,
      default: false,
    },
    modelValue: {
      type: Boolean,
      required: true,
    },
    appendToBody: {
      type: Boolean,
      default: false,
    },
    fullscreen: {
      type: Boolean,
      default: false,
    },
    fullscreenSize: {
      default: "",
      validator(v: string) {
        return ["", "sm", "md", "lg", "xl", "xxl"].indexOf(v) !== -1;
      },
    },
    bodyIcon: String,
  },
  setup(props: any, ctx: SetupContext) {
    const innerButtons = ref<DialogButtonProps[]>(initButtons(props.buttons));
    const visible = ref<boolean>(false);
    const zIndex = ref<boolean | null>(null);
    const preShowStatic = ref<boolean>(false);
    const showStatic = ref<boolean>(false);
    //窗口是否正在打开中
    let isOpening = false;
    //窗口是否正在关闭中
    let isClosing = false;
    function beforeEnter() {
      isOpening = true;
    }
    function afterEnter() {
      isOpening = false;
      ctx.emit(OPENED_EVENT);
    }
    function afterLeave() {
      isClosing = false;
      ctx.emit(CLOSED_EVENT);
      ctx.emit(UPDATE_MODEL_EVENT, false);
    }
    function transitionend() {
      if (showStatic.value) {
        showStatic.value = false;
      } else {
        preShowStatic.value = false;
      }
    }
    const dialogOper: DialogOper = {
      id: DIALOG_ID++,
      setZIndex(value: boolean | null) {
        zIndex.value = value;
      },
    };
    function open() {
      if (visible.value) return;
      visible.value = true;
      ctx.emit(OPEN_EVENT);
      Modal.push(dialogOper);
    }
    function close() {
      if (!visible.value) return;
      visible.value = false;
      isClosing = true;
      ctx.emit(CLOSE_EVENT);
      Modal.remove(dialogOper.id);
    }
    function onModalClick(e: Event) {
      if (!(e.target as HTMLElement).classList.contains("modal") || isClosing) {
        return;
      }
      if (props.static) {
        //打开动画还没结束 不能开始静态动画效果
        if (!isOpening) {
          preShowStatic.value = true;
          showStatic.value = true;
        }
        return;
      }
      close();
    }
    function onBtnClick(btn: DialogButtonProps) {
      if (isClosing) return;
      var handler = btn.handler;
      let re = null;
      if (handler) {
        re = handler(btn);
      }
      if (re !== false && btn.close) {
        close();
      }
    }
    watch(
      () => props.modelValue,
      (v) => {
        if (v) {
          open();
        } else {
          close();
        }
      }
    );
    const isContentVNode = computed(() => isVNode(props.content));
    onBeforeMount(() => {
      Modal.init();
    });
    onBeforeUnmount(() => {
      Modal.remove(dialogOper.id);
    });
    onMounted(() => {
      if (props.modelValue) {
        open();
      }
    });
    function toggleAllButtons(disabled: boolean) {
      innerButtons.value.forEach((btn) => {
        console.log(btn);
        btn.disabled = disabled;
      });
    }
    const disableAllButtons = function () {
      toggleAllButtons(true);
    };
    const enableAllButtons = function () {
      toggleAllButtons(false);
    };
    return {
      id: DIALOG_ID,
      visible,
      zIndex,
      preShowStatic,
      showStatic,
      beforeEnter,
      afterEnter,
      afterLeave,
      onModalClick,
      onBtnClick,
      close,
      open,
      transitionend,
      isContentVNode,
      innerButtons,
      disableAllButtons,
      enableAllButtons,
    };
  },
});
</script>
<style>
/*modal样式默认是display:none,解决无法用v-show控制显示的问题*/
.vb-dialog {
  display: block;
}
.vb-dialog-body-ii {
  padding-right: 1rem;
}
</style>
